/*
 ============================================================================
 Name        : main.cpp
 Author      : mattia
 Version     :
 Copyright   : Your copyright notice
 Description : Compute Pi in MPI C++
 ============================================================================
 */

#include <stdio.h>
#include <openmpi/mpi.h>
#include <pthread.h>
#include <time.h>
#include <ctime>
#include <string>
#include "DppmSpec.h"
#include "Config.h"
#include "Utils.h"
#include "SlaveRoutine.h"
#include "MasterRoutine.h"
#include "Tuple.h"

using namespace std;

int rank, size;
DppmSpec * dppm;

int main(int argc, char ** argv) {

	int numAct, T;
	int* numPreds;
	int* numModes;
	int** Preds;
	int** ModesCosts;
	int** ModesTimes;

	//Inicializar MPI
	MPI_Init(&argc , &argv );

	//Inicializar variables globales
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	debugPrinter(0, rank, "main", 51, "Inicializando MPI...","size",size);

	//El master lee la especificacion del DPPM desde el arhivo pasado por parametro
	if (!rank) {
		if ((argc != 2)
				|| readDppmSpec(argv[1], numAct, T, numPreds, Preds, numModes,
						ModesCosts, ModesTimes)) {
			printf("Error!!\n");
			return -1;
		}
		debugPrinter(0, rank, "main", 70, "Leido DppmParams info");
	}

	//Comunica la especificacion
	MPI_Bcast(&numAct, 1, MPI_INT, MASTER_RANK, MPI_COMM_WORLD);
	MPI_Bcast(&T, 1, MPI_INT, MASTER_RANK, MPI_COMM_WORLD);

	if (rank) {
		numPreds = new int[numAct];
		numModes = new int[numAct];
		Preds = new int*[numAct];
		ModesCosts = new int*[numAct];
		ModesTimes = new int*[numAct];
	}

	MPI_Bcast(numPreds, numAct, MPI_INT, MASTER_RANK, MPI_COMM_WORLD);
	MPI_Bcast(numModes, numAct, MPI_INT, MASTER_RANK, MPI_COMM_WORLD);

	for (int i = 0; i < numAct; i++) {
		if (rank) {
			Preds[i] = new int[numPreds[i]];
			ModesCosts[i] = new int[numModes[i]];
			ModesTimes[i] = new int[numModes[i]];
		}
		MPI_Bcast(Preds[i], numPreds[i], MPI_INT, MASTER_RANK, MPI_COMM_WORLD);
		MPI_Bcast(ModesCosts[i], numModes[i], MPI_INT, MASTER_RANK,
				MPI_COMM_WORLD);
		MPI_Bcast(ModesTimes[i], numModes[i], MPI_INT, MASTER_RANK,
				MPI_COMM_WORLD);
	}

	//Crear la variable dppm
	dppm = new DppmSpec(numAct, T, numPreds, Preds, numModes, ModesCosts,	ModesTimes);

	//Inicializar el tamanio de las tuplas
	Tuple::SetTuplesIntSize(numAct);

	//Procedimiento que selecciona equitativamenteel rango inicial del arbol en base a rank, size
	//y la especificacion del caso DPPM (numero de actividades y numeros de modos por actividad).
	if (rank) {

		/***********MEDICION DEL TIEMPO OBTENIDO**************/
		time_t tic = time(0);
		string aux("INICIO PROCESSING!  At ");
		aux.append(ctime(&tic));
		debugPrinter(0, rank, "main", 198,
				aux.substr(0, aux.length() - 1).c_str());
		/*****************************************************/

		int iter = SlaveRoutine();

		/***********MEDICION DEL TIEMPO OBTENIDO**************/
		time_t toc = time(0);
		aux = string("FIN PROCESSING!  At ");
		aux.append(ctime(&toc));
		int timeElapsed = toc - tic;
		debugPrinter(0, rank, "main", 198,
				aux.substr(0, aux.length() - 1).c_str(),
				"Tiempo utilizado (en segundos)", timeElapsed,
				"Iteraciones:",iter);
		/*****************************************************/

	} else {

//		int debug = 1;
//		while (debug);

		MasterRoutine();

	}

	MPI_Finalize();

	delete [] numPreds;
	delete [] Preds;
	delete [] numModes;
	delete [] ModesCosts;
	delete [] ModesTimes;
	delete dppm;

	return 0;
}

